﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
namespace GX
{
    public class NetManager : INetManager
    {
        private const int NUM_TRY_MAX = 3;
        private int _numTried = 0;
        private string _ip;
        private int _port;
        private Socket _sock;
        private Dictionary<ushort, INetHandler> _handlerDic = new Dictionary<ushort, INetHandler>();
        private NetSender _sender = new NetSender();
        private NetReciever _reciever = new NetReciever();

        private bool _needExePackage;
        private readonly System.Object _locker = new System.Object();
        public Action<NetManager, string> onConnect;
        public Action<NetManager> onDisConnect;
        public Action<NetManager, IPackageIn> onNetData;
        public Action<NetManager> onClose;
        public Action<NetManager, string> onError;
        public bool isConnected
        {
            get
            {
                return (null != _sock) && (_sock.Connected);
            }
        }
        public bool IsSameSocket(string ip, int port)
        {
            return (this._ip == ip) && (this._port == port);
        }
        public void Connect(string ip, int port)
        {
            if (null == _sock)
            {
                _sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _sock.NoDelay = true;
                _sock.ReceiveBufferSize = NetConfig.BUFFER_SIZE;
                _sock.SendBufferSize = NetConfig.BUFFER_SIZE;
            }
            /**已经连接上不管，如果需要连接别的服务器需要手动断开重连
             * **/
            if (this.isConnected)
            {
                return;
            }

            _numTried = 0;
            _ip = ip;
            _port = port;
            DoConnect();

        }
        private void DoConnect()
        {
            try
            {
                IPAddress address = IPAddress.Parse(_ip);
                IPEndPoint ipEndPoint = new IPEndPoint(address, _port);
                _sock.BeginConnect(ipEndPoint, OnConnect, this);

            }
            catch (Exception e)
            {
                OnConnect(e.Message);
            }
        }
        void OnConnect(string error)
        {
            if (null != onConnect)
            {
                onConnect(this, error);
            }
        }
        private void OnConnect(IAsyncResult ar)
        {
            NetManager nm = ar.AsyncState as NetManager;
            ++_numTried;
            if (this.isConnected)
            {
                _numTried = 0;
                _sock.EndConnect(ar);
                _sender.Setup(this, _sock);
                _reciever.Setup(this, _sock);
                OnConnect(string.Empty);
            }
            else if (_numTried >= NUM_TRY_MAX)
            {
                _numTried = 0;
                _sock.EndConnect(ar);
                OnConnect("failed to connect");
            }
            else
            {
                DoConnect();
            }
        }
        public void Disconnect(bool reuseSocket = true)
        {
            if (this.isConnected)
            {
                _sock.Shutdown(SocketShutdown.Both);
                _sock.Disconnect(reuseSocket);
                if (null != onDisConnect)
                {
                    onDisConnect(this);
                }
            }
        }
        public void AddNetHandler(INetHandler handler)
        {
            if (null == _handlerDic) return;
            if (!_handlerDic.ContainsKey(handler.code))
            {
                _handlerDic.Add(handler.code, handler);
            }
            INetHandler oldHandler = _handlerDic[handler.code];
            _handlerDic[handler.code] = handler;
        }
        public void RemoveNetHandler(ushort code)
        {
            if (null == _handlerDic) return;
            if (!_handlerDic.ContainsKey(code)) return;
            _handlerDic.Remove(code);
        }
        internal void HandlePackage(PackageIn pkg)
        {
            if (_handlerDic.ContainsKey(pkg.code))
            {
                _handlerDic[pkg.code].HandlePackage(pkg);
            }
            else
            {
                if (null != onNetData)
                    onNetData(this, pkg);
            }
        }
        public IPackageOut GetPkgOut(byte frame, byte way)
        {
            PackageOut pkg = new PackageOut();
            pkg.InitHead(frame, way);
            return pkg;
        }

        public void Send(IPackageOut pkg)
        {
            if (null != this._sender)
            {
                _sender.Send(pkg);
            }
        }
        public void Recieve()
        {
            lock (_locker)
            {
                if (null == this._reciever) return;

                if (this._needExePackage)
                {
                    _reciever.ExePackage();
                }
                else
                {
                    _reciever.Recieve();
                }
                this._needExePackage = !this._needExePackage;
            }
        }

        public void Dispose()
        {
            if (null != _sock)
            {
                _sock.Shutdown(SocketShutdown.Both);
                _sock.Close();
                _sock = null;
            }
        }

    }
}